﻿using System.Collections.Generic;
using BeefyEventAggregator;
using MATtutsLab.Entities;
using MATtutsLab.MVP.Events;
using MATtutsLab.MVP.Presenters;
using MATtutsLab.MVP.Views;
using MATtutsLab.Repositories;
using NUnit.Framework;
using Rhino.Mocks;

namespace MATtutsLab.Specs
{
    public abstract class ZombieMoviePresenterSpecsContext
    {
        protected ZombieMoviePresenter Presenter;
        protected IZombieMovieCatalogView View;
        protected IMovieRepository Repository;
        protected IEventPublisher EventPublisher;

        [TestFixtureSetUp]
        protected virtual void Context()
        {
            View = MockRepository.GenerateMock<IZombieMovieCatalogView>();
            Repository = MockRepository.GenerateMock<IMovieRepository>();
            EventPublisher = MockRepository.GenerateMock<IEventPublisher>();

            Presenter = new ZombieMoviePresenter(View, Repository, EventPublisher, null, null, null);
        }
    }

    [TestFixture]
    public class WhenInitializingViewAndZombieMovieDataExists : ZombieMoviePresenterSpecsContext
    {
        protected override void Context()
        {
            base.Context();

            Repository.Stub(repo => repo.GetAll()).Return(new List<Movie> { new Movie() });

            Presenter.InitializeView();
            Presenter.MovieSelected(new Movie());
        }

        [Test]
        public void Should_get_list_of_zombie_movies()
        {
            Repository.AssertWasCalled(repo => repo.GetAll());
        }

        [Test]
        public void Should_display_list_of_zombie_movies()
        {
            View.AssertWasCalled(view => view.DisplayMovies(null), options => options.IgnoreArguments());
        }

        [Test]
        public void Should_select_first_movie_in_the_list()
        {
            View.SelectFirstMovieInList();
        }

        [Test]
        public void Should_show_the_movie_cover_image_for_initially_selected_movie()
        {
            View.AssertWasCalled(view => view.ShowMovieCoverImage(null), options => options.IgnoreArguments());
        }
    }

    [TestFixture]
    public class WhenInitializingViewAndZombieMovieDataDoesNotExist : ZombieMoviePresenterSpecsContext
    {
        protected override void Context()
        {
            base.Context();

            Repository.Stub(repo => repo.GetAll()).Return(new List<Movie> { new Movie() });

            Presenter.InitializeView();
            Presenter.MovieSelected(null);
        }

        [Test]
        public void Should_NOT_show_the_movie_cover_image_for_initially_selected_movie()
        {
            View.AssertWasNotCalled(view => view.ShowMovieCoverImage(null), options => options.IgnoreArguments());
        }
    }

    [TestFixture]
    public class WhenSelectingAZombieMovie : ZombieMoviePresenterSpecsContext
    {
        protected override void Context()
        {
            base.Context();

            Presenter.MovieSelected(new Movie());

        }

        [Test]
        public void Should_show_the_movie_cover_image()
        {
            View.AssertWasCalled(view => view.ShowMovieCoverImage(null), options => options.IgnoreArguments());
        }
    }

    [TestFixture]
    public class WhenAttemptingToShowCoverImageAndZombieMovieIsNull : ZombieMoviePresenterSpecsContext
    {
        protected override void Context()
        {
            base.Context();

            Presenter.MovieSelected(null);
        }

        [Test]
        public void Should_not_load_movie_image()
        {
            View.AssertWasNotCalled(view => view.ShowMovieCoverImage(null), options => options.IgnoreArguments());
        }
    }

    [TestFixture]
    public class WhenSelectingToViewMovieDetails : ZombieMoviePresenterSpecsContext
    {
        private bool openViewDetailsActionWasCalled;

        protected override void Context()
        {
            base.Context();

            Presenter = new ZombieMoviePresenter(View, Repository, null, openViewDetailsAction, null, null);

            Presenter.InitializeView();
            Presenter.MovieSelected(new Movie());
            Presenter.ViewDetailsRequested();
        }

        private void openViewDetailsAction(Movie movie)
        {
            openViewDetailsActionWasCalled = true;
        }

        [Test]
        public void Should_display_movie_details()
        {
            Assert.IsTrue(openViewDetailsActionWasCalled);
        }
    }

    [TestFixture]
    public class WhenRequestingToAddAMovie : ZombieMoviePresenterSpecsContext
    {
        private bool openAddMovieActionWasCalled;

        protected override void Context()
        {
            base.Context();

            Presenter = new ZombieMoviePresenter(View, Repository, null, null, openAddMovieAction, null);

            Presenter.InitializeView();
            Presenter.AddMovieRequested();
        }

        private void openAddMovieAction()
        {
            openAddMovieActionWasCalled = true;
        }

        [Test]
        public void Should_display_add_movie_screen()
        {
            Assert.IsTrue(openAddMovieActionWasCalled);
        }
    }

    [TestFixture]
    public class WhenRequestingToEditAMovie : ZombieMoviePresenterSpecsContext
    {
        private bool openEditMovieActionWasCalled;

        protected override void Context()
        {
            base.Context();

            Presenter = new ZombieMoviePresenter(View, Repository, null, null, null, openEditMovieAction);

            Presenter.InitializeView();
            Presenter.MovieSelected(new Movie());
            Presenter.EditMovieRequested();
        }

        private void openEditMovieAction(Movie movie)
        {
            openEditMovieActionWasCalled = true;
        }

        [Test]
        public void Should_display_edit_movie_screen()
        {
            Assert.IsTrue(openEditMovieActionWasCalled);
        }
    }

    [TestFixture]
    public class WhenRequestingToDeleteAMovieAndMovieIsSelectedAndUserConfirmsToDelete : ZombieMoviePresenterSpecsContext
    {
        [TestFixtureSetUp]
        protected override void Context()
        {
            base.Context();

            View.Expect(view => view.PromptUserForDeleteConfirmation()).Return(true);

            Presenter.InitializeView();
            Presenter.MovieSelected(new Movie());
            Presenter.DeleteRequested();
        }

        [Test]
        public void Should_prompt_user_for_confirmation()
        {
            View.AssertWasCalled(view => view.PromptUserForDeleteConfirmation());
        }

        [Test]
        public void Should_delete_movie()
        {
            Repository.AssertWasCalled(repo => repo.Delete(null), options => options.IgnoreArguments());
        }

        [Test]
        public void Should_refresh_movie_catalog()
        {
            EventPublisher.AssertWasCalled(eventPublisher => eventPublisher.Publish(new MovieUpdatedEvent(null)), options => options.IgnoreArguments());
        }
    }

    [TestFixture]
    public class WhenRequestingToDeleteAMovieAndMovieIsNOTSelected : ZombieMoviePresenterSpecsContext
    {
        [TestFixtureSetUp]
        protected override void Context()
        {
            base.Context();

            Presenter.InitializeView();
            Presenter.MovieSelected(null);
            Presenter.DeleteRequested();
        }

        [Test]
        public void Should_not_prompt_user_for_confirmation()
        {
            View.AssertWasNotCalled(view => view.PromptUserForDeleteConfirmation());
        }

        [Test]
        public void Should_NOT_delete_movie()
        {
            Repository.AssertWasNotCalled(repo => repo.Delete(null), options => options.IgnoreArguments());
        }
    }


    [TestFixture]
    public class WhenHandlingMovieUpdatedEvent : ZombieMoviePresenterSpecsContext
    {
        protected override void Context()
        {
            base.Context();

            View.Stub(view => view.PromptUserForDeleteConfirmation()).Return(true).Repeat.Any();
            EventPublisher.Stub(eventpublisher => eventpublisher.Publish(new MovieUpdatedEvent(new Movie()))).Repeat.Any
                ();


            Presenter.InitializeView();
            Presenter.MovieSelected(new Movie());
            Presenter.DeleteRequested();
            Presenter.Handle(new MovieUpdatedEvent(new Movie()));
        }

        [Test]
        public void Should_refresh_movie_list()
        {
            View.AssertWasCalled(view => view.RefreshMovieList(null, null), options => options.IgnoreArguments());
        }
    }
}
